package com.denghq.projectbuilder.component.syslog.autoconfigure;


import com.denghq.projectbuilder.common.CodeMsg;
import com.denghq.projectbuilder.common.Constant;
import com.denghq.projectbuilder.common.ResultInfo;
import com.denghq.projectbuilder.common.exception.BussinessException;
import com.denghq.projectbuilder.common.util.BeanTool;
import com.denghq.projectbuilder.component.remote.model.SysLogModel;
import com.denghq.projectbuilder.component.remote.model.UserRightViewModel;
import com.denghq.projectbuilder.component.remote.utils.HttpContextUtils;
import com.denghq.projectbuilder.component.remote.utils.UserContextHolder;
import com.denghq.projectbuilder.component.syslog.enums.ResponseEnum;
import com.denghq.projectbuilder.component.syslog.thread.SysLogHolder;
import com.denghq.projectbuilder.component.syslog.annotation.SysLog;
import com.google.common.collect.Lists;
import com.specialconfig.FeignTokenSupportConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.*;


/**
 * 操作日志，切面处理类
 *
 * @author denghq
 */
@Aspect
@Slf4j
public class SysLogAspect {

    //@Value("${spring.application.name}")
    //private String appNo;

    public static final String FILE_PARAM_MARK = "【file】ARRAY";
    public static final String FILE_ARRAY_PARAM_MARK = "【file】";

    @Autowired
    private RestTemplateBuilder restTemplateBuilder;

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @Pointcut("@annotation(com.denghq.projectbuilder.component.syslog.annotation.SysLog)")
    public void logPointCut() {

    }

    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {

        //执行方法,result为执行结果
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        final Object[] args = point.getArgs();//参数
        Object beforeDetailData = null;
        SysLog syslog = method.getAnnotation(SysLog.class);

        //检查权限
        checkPermission(syslog);

        if (syslog != null && syslog.isEdit()) {//修改操作需要拿到保存之前的结果
            final String idPropertyName = syslog.idPropertyName();//id字段
            final String dataUrl = syslog.detailDataUrl();//详情接口
            beforeDetailData = getDetailData(idPropertyName, dataUrl, args);
        }
        long beginTime = System.currentTimeMillis();
        Object result = point.proceed();
        //执行时长(毫秒)
        long time = System.currentTimeMillis() - beginTime;
        //保存日志
        if (syslog != null && syslog.isSaveLog()) {
            saveSysLog(syslog, args, time, result, beforeDetailData);
        }

        return result;
    }

    private void checkPermission(SysLog syslog) {
        if (syslog != null && syslog.isCheckPermission()) {
            UserRightViewModel userInfo = UserContextHolder.getUserInfo();
            //检查token是否失效
            if (userInfo == null) {
                throw new BussinessException(CodeMsg.API_UNAUTHORIZED, "未登录");
            }
            //TODO 检查权限
        }
    }

    /**
     * 获取数据详情信息，返回json字符串
     *
     * @param idPropertyName
     * @param dataUrl
     * @param args
     * @return
     */
    private Object getDetailData(String idPropertyName, String dataUrl, Object[] args) {
        String path = buildRequestAddress(dataUrl, idPropertyName, args);
        if (StringUtils.isNotBlank(path)) {
            RestTemplate restTemplate = restTemplateBuilder.build();
            HttpHeaders headers = new HttpHeaders();
            MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
            headers.setContentType(type);
            headers.add("Accept", MediaType.APPLICATION_JSON.toString());
            //添加全局有效token
            headers.add(Constant.SERVICE_TOKEN_NAME, FeignTokenSupportConfig.SPECIAL_AUTHORIZATION);

            HttpEntity<String> entity = new HttpEntity<>(null, headers);
            ResponseEntity<String> exchange = restTemplate.exchange(path,
                    HttpMethod.GET, entity, String.class);
            String resultMsg = exchange.getBody();

            //String resultMsg = restTemplate.getForObject(path, String.class);
            Map map = BeanTool.jsonStrToObject(resultMsg, Map.class);
            if (CodeMsg.SUCCESS.getCode() == Integer.parseInt(map.get("code").toString())) {
                return map.get("result");
            } else {
                return null;
            }
        }
        return null;
    }

    /**
     * 构建查询详情的请求地址
     *
     * @param dataUrl
     * @param idPropertyName
     * @param args
     * @return
     */
    private String buildRequestAddress(String dataUrl, String idPropertyName, Object[] args) {

        StringBuilder pathBuilder = new StringBuilder();
        if (StringUtils.isNotBlank(dataUrl)) {

            if (StringUtils.isNotBlank(dataUrl)) {
                if (dataUrl.trim().startsWith("http")) {
                    pathBuilder
                            .append(dataUrl);
                } else {
                    if (dataUrl.trim().startsWith("/")) {
                        dataUrl = dataUrl.substring(1);
                    }
                    ServiceInstance serviceInstance = loadBalancerClient.choose(dataUrl.substring(0, dataUrl.indexOf("/")));
                    if (serviceInstance == null) {
                        log.warn("获取数据详情接口地址出错，检查detailDataUrl配置，detailDataUrl：{}", dataUrl);
                        return null;
                    }
                    pathBuilder
                            .append("http://")
                            .append(serviceInstance.getHost())
                            .append(":")
                            .append(serviceInstance.getPort())
                            .append("/")
                            .append(dataUrl);
                }
            }

            if (!dataUrl.contains("?")) {
                pathBuilder.append("?id=");
            }
        } else {
            log.warn("获取数据详情接口地址为空");
        }
        pathBuilder.append(getIdProperty(idPropertyName, args));
        return pathBuilder.toString();
    }

    private String getIdProperty(String idPropertyName, Object[] args) {
        Map finalArg = null;
        //1.过滤无效字段
        if (args != null) {
            for (Object arg : args) {

                if (arg instanceof HttpServletRequest) {
                    continue;
                }

                if (arg instanceof HttpServletResponse) {
                    continue;
                }

                if (arg instanceof MultipartFile) {
                    continue;
                }
                //排除 List<MultipartFile>
                if (arg instanceof Collection) {
                    continue;
                }
                //排除 MultipartFile[]
                if (arg.getClass().isArray()) {
                    continue;
                }
                finalArg = BeanTool.objectToMap(arg);
            }

        }
        //2.获取id属性
        if (finalArg != null) {
            final Object idProperty = finalArg.get(idPropertyName);
            if (idProperty != null) {
                return idProperty.toString();
            }
        }
        return null;
    }

    private void saveSysLog(SysLog syslog, Object[] args, long elapsedMilliseconds, Object result, Object beforeDetailData) {

        if (syslog != null) {

            String authorization = UserContextHolder.getAuthorization();
            if (FeignTokenSupportConfig.SPECIAL_AUTHORIZATION.equals(authorization)) { //过滤掉永久token发起的请求-属于服务间调用，不是单独的操作
                return;
            }

            SysLogModel sysLogEntity = new SysLogModel();
            UserRightViewModel userInfo = null;
            try {
                userInfo = UserContextHolder.getUserInfo();
            } catch (Exception e) {
            }

            if (userInfo != null) {
                sysLogEntity.setUserCode(userInfo.getUserCode());
                sysLogEntity.setUserName(userInfo.getUserName());
                sysLogEntity.setDepartmentId(userInfo.getDepartmentId());
            } else {//过滤掉无用户操作
                return;
            }

            //sysLogEntity.setAppNo(appNo);
            HttpServletRequest req = HttpContextUtils.getHttpServletRequest();
            sysLogEntity.setHostIP(getRemoteHost(req));
            sysLogEntity.setFunctionCode(syslog.functionCode());
            //sysLogEntity.setFunctionName();
            sysLogEntity.setActionCode(syslog.actionCode());
            //sysLogEntity.setActionName();
            sysLogEntity.setCreatedTime(new Date());
            //sysLogEntity.setContent();//保存逻辑中处理
            sysLogEntity.setRemark(syslog.remark());

            //请求的参数， 过滤掉文件
            sysLogEntity.setRequestParam(getArgs(args));
            //响应结果，result
            setContentAndResponse(sysLogEntity, syslog, result, args, beforeDetailData);
            sysLogEntity.setElapsedMilliseconds(elapsedMilliseconds);
            //保存日志
            doSave(sysLogEntity);
        }

    }

    private void setContentAndResponse(SysLogModel sysLogEntity, SysLog syslog, Object result, Object[] args, Object beforeDetailData) {
        sysLogEntity.setResponse(ResponseEnum.success.getName());
        sysLogEntity.setContent("操作成功");
        //增、删、改（前-后）、查
        try {
            ResultInfo resultInfo = (ResultInfo) result;
            if (resultInfo.getCode() == 0) {
                sysLogEntity.setResponse(ResponseEnum.success.getName());
                final boolean edit = syslog.isEdit();//编辑接口
                if (edit) {//修改
                    //编辑接口，保存编辑前和编辑后结果
                    final String idPropertyName = syslog.idPropertyName();
                    final String dataUrl = syslog.detailDataUrl();//详情接口
                    //拿到修改后数据
                    final Object finalDetailData = getDetailData(idPropertyName, dataUrl, args);
                    if (beforeDetailData != null && finalDetailData != null) {
                        Map<String, Object> editInfo = new HashMap<>();
                        editInfo.put("before", beforeDetailData);
                        editInfo.put("after", finalDetailData);
                        sysLogEntity.setContent(BeanTool.objectToJsonStr(editInfo));
                    } else {
                        sysLogEntity.setContent("操作成功");
                        log.error("操作日志：获取数据详情出错，syslog:{}", syslog);
                    }

                } else {
                    final Object resultData = resultInfo.getResult();
                    if (resultData == null) {
                        sysLogEntity.setContent("操作成功");
                    } else if (resultData instanceof Collection) {//查多条
                        Collection res = (Collection) resultData;
                        sysLogEntity.setContent("共" + resultInfo.getTotalCount() + "条数据，当前查询" + res.size() + "条。");
                    } else if (resultData.getClass().isArray()) {
                        final int length = Array.getLength(resultData);
                        sysLogEntity.setContent("共" + resultInfo.getTotalCount() + "条数据，当前查询" + length + "条。");
                    } else {//详情，或删除
                        sysLogEntity.setContent("操作成功");
                    }
                }
            } else {
                sysLogEntity.setResponse(ResponseEnum.error.getName());
                sysLogEntity.setContent("请求失败：" + resultInfo.getMessage() + ",code:" + resultInfo.getCode() + ",codeRemark" + resultInfo.getCodeRemark());
            }

        } catch (Exception e) {
            log.debug("返回结果不为ResultInfo类型，result:{}", result);
        }

    }

    //保存日志
    private void doSave(SysLogModel sysLogEntity) {
        SysLogHolder.getInstance().putOne(sysLogEntity);
    }

    //请求的参数， 过滤掉文件
    private String getArgs(Object[] args) {
        List<Object> argList = Lists.newArrayList();
        for (Object arg : args) {
            if (arg instanceof HttpServletRequest) {
                continue;
            }

            if (arg instanceof HttpServletResponse) {
                continue;
            }

            if (arg instanceof MultipartFile) {
                argList.add(FILE_PARAM_MARK);
                continue;
            }
            //排除 List<MultipartFile>
            if (arg instanceof Collection) {
                //arg
                Collection argCollection = (Collection) arg;
                Iterator iterator = argCollection.iterator();
                if (iterator.hasNext()) {
                    Object next = iterator.next();
                    if (next instanceof MultipartFile) {
                        argList.add(FILE_ARRAY_PARAM_MARK);
                    } else {
                        argList.add(arg);
                    }
                }
                continue;
            }
            //排除 MultipartFile[]
            if (arg.getClass().isArray()) {
                //arg
                Class genericType = arg.getClass().getComponentType();
                if (MultipartFile.class.isAssignableFrom(genericType)) {
                    argList.add(FILE_ARRAY_PARAM_MARK);
                    continue;
                }
            }

            argList.add(arg);

        }
        return BeanTool.objectToJsonStr(argList);
    }

    private String getRemoteHost(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip.equals("0:0:0:0:0:0:0:1") ? "127.0.0.1" : ip;
    }

    public static void main1(String[] args) {
        List<Object> obj = Lists.newArrayList();

        obj.add("0");
        //System.out.println(obj instanceof Collection);
        Collection o = (Collection) obj;
        Iterator iterator = o.iterator();
        Object next = iterator.next();
        System.out.println(next instanceof MultipartFile);

        System.out.println(MultipartFile.class.isAssignableFrom(next.getClass()));

        Object[] obj1 = {};
        System.out.println("=======" + obj1.getClass().isArray());
        //System.out.println(genericType.getSimpleName());
        Class genericType = obj1.getClass().getComponentType();

        System.out.println(MultipartFile.class.isAssignableFrom(genericType));
    }

}
